#nullable disable

using System.Collections.Concurrent;
using System.Globalization;
using System.Text.RegularExpressions;

namespace System;
/// <summary>
/// LinqExtensions
/// </summary>
public static class LinqExtensions
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="source"></param>
    /// <param name="propertyName"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static IEnumerable<T> OrderByNatural<T>(
        this IEnumerable<T> source, string propertyName)
    {
        if (source is null)
            throw new ArgumentNullException(nameof(source));

        if (propertyName is null)
            throw new ArgumentNullException(nameof(propertyName));

        try
        {
            return source.OrderBy(x => x.GetReflectedPropertyValue(propertyName), new NaturalSortComparer<T>());
        }
        catch
        { }

        return source;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="subject"></param>
    /// <param name="field"></param>
    /// <returns></returns>
    public static string GetReflectedPropertyValue(this object subject, string field)
    {
        object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null);
        return reflectedValue is not null ? reflectedValue.ToString() : "";
    }
}
/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam> 
public class NaturalSortComparer<T> : IComparer<string>, IDisposable
{
    /// <summary>
    /// Regular expression used to identify which part of the string may be a number (decimal).
    /// </summary>
    private const string RegexString = @"([0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?)";

    /// <summary>
    /// Type of comparaison used to compare strings between them
    /// </summary>
    private const StringComparison Ordinal = StringComparison.Ordinal;
    /// <summary>
    ///
    /// </summary>
    private ConcurrentDictionary<string, string[]> table = new();
    private readonly bool isAscending;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="isAscendingOrder"></param>
    public NaturalSortComparer(bool isAscendingOrder = true)
    {
        this.isAscending = isAscendingOrder;
    }

    int IComparer<string>.Compare(string x, string y)
    {
        if (x == y)
            return 0;
        if (string.IsNullOrEmpty(x) || string.IsNullOrEmpty(y))
            return -1;

        string[] x1, y1;

        if (!table.TryGetValue(x, out x1))
        {
            x1 = Regex.Split(x.Replace(" ", ""), RegexString);
            table.TryAdd(x, x1);
        }

        if (!table.TryGetValue(y, out y1))
        {
            y1 = Regex.Split(y.Replace(" ", ""), RegexString);
            table.TryAdd(y, y1);
        }

        int returnVal;

        for (int i = 0; i < x1.Length && i < y1.Length; i++)
        {
            if (x1[i] != y1[i])
            {
                returnVal = PartCompare(x1[i], y1[i]);
                return isAscending ? returnVal : -returnVal;
            }
        }

        if (y1.Length > x1.Length)
        {
            returnVal = 1;
        }
        else if (x1.Length > y1.Length)
        {
            returnVal = -1;
        }
        else
        {
            returnVal = 0;
        }

        return isAscending ? returnVal : -returnVal;
    }

    private static int PartCompare(string left, string right)
    {
        decimal x, y;

        if (!decimal.TryParse(left, NumberStyles.Any, CultureInfo.InvariantCulture, out x))
            return String.Compare(left, right, Ordinal);

        if (!decimal.TryParse(right, NumberStyles.Any, CultureInfo.InvariantCulture, out y))
            return String.Compare(left, right, Ordinal);

        return x.CompareTo(y);
    }
    /// <summary>
    /// 
    /// </summary>
    public void Dispose()
    {
        table.Clear();
    }
}